/*
 * PlantillaComponente.java
 *
 * Copyright 2011 Universidad Francisco de Paula Santander (UFPS).
 * Todos los Derechos Reservados.
 * Creado el 15/12/2010, 11:23:38 PM
 */

package com.ufps.cuatro.modelo.representacion;

import com.ufps.cuatro.controlador.render.UtilRender;
import com.ufps.cuatro.modelo.representacion.componente.Componente;
import com.ufps.cuatro.modelo.representacion.componente.ComponenteDistribuidor;
import com.ufps.cuatro.util.base.ObjectBase;
import java.awt.GridBagConstraints;
import java.util.ArrayList;
import java.util.Collections;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * <i><b>PlantillaComponente:</b></i>Esta Clase define configuraciones para 
 * distribuir y ubicar un Componente en un área de interfaz gráfica.
 * 
 * @author Carlos A. Torres C. - Dolly E. Guerrero. <https://code.google.com/p/tesis4dor/>
 * @version 1.0 15/12/2010
 * @see ObjectBase  
 */
public class PlantillaComponente extends ObjectBase{
    private static final long serialVersionUID = -5117010616234672204L;
    
    /**
     * 
     */
    public final static int SIN_DEFINIR = -1;
    
    /**
     * 
     */
    public final static int GENERADO_INDEPENDIENTE = 0;
    /**
     * 
     */
    public final static int GENERADO_DEPENDIENTE = 1;
    /**
     * 
     */
    public final static int GENERADO_INTERNO = 2;
    
    /**
     * 
     */
    public final static int GENERADOR_DEPENDIENTE_METODO_RETURN = 3;
    /**
     * 
     */
    public final static int GENERADOR_DEPENDIENTE_METODO_PARAM = 4;
    /**
     * 
     */
    public final static int GENERADOR_DEPENDIENTE_CLASE_METODO = 5;
    /**
     * 
     */
    public final static int GENERADOR_DEPENDIENTE_CLASE_ATRIBUTO = 6;
    /**
     * 
     */
    public final static int GENERADOR_DEPENDIENTE_INTEGRADOR_RETURN = 7;
    /**
     * 
     */
    public final static int GENERADOR_DEPENDIENTE_INTEGRADOR_PARAM = 8;   
    
    /**
     * 
     */
    public final static int INTEGRADOR_DEPENDIENTE_ASISTENTE_LIBRE = 9;
    /**
     * 
     */
    public final static int INTEGRADOR_DEPENDIENTE_ASISTENTE_ESPEJO = 10;
    
    /**
     * 
     */
    public final static int EXTRA_DEPENDIENTE_ASISTENTE_LIBRE = 11;
    /**
     * 
     */
    public final static int EXTRA_DEPENDIENTE_INTEGRADOR_EXTRA = 12;
    /**
     * 
     */
    public final static int DISTRIBUIDOR_DEPENDIENTE_INTEGRADOR_BEANS = 13;
    
    public final static int DISTRIBUIDOR_OBJECT_MODELO_ASOCIADO = 14;
    public final static int DISTRIBUIDOR_KEY_MODELO_ASOCIADO = 15;
    
    private int idPlantilla;
    private int idConstructor;
    private int idComponente;
    private int x;
    private int y;
    private int width;
    private int height;
    private int zona;
    private int representacion = SIN_DEFINIR;
    
    private boolean generador;
    private boolean requerido;    
    private boolean incluido; 

    private ObjetoConstructor objConstructor;
    
    private ArrayList<String> objParametrosConstructor;       
    
    private GridBagConstraints objGridBagConstraints;
    
    private ArrayList<InternalProperty> objInternalProperties;

    /**
     * Constructor por defecto
     */
    public PlantillaComponente() {
        this.objGridBagConstraints = new GridBagConstraints();
        this.objInternalProperties = new ArrayList<InternalProperty>();
    } 
    
    /**
     * Constructor que inicializa propiedades y la referencia a la Plantilla Padre.
     * @param id Propiedad utilizada para identificar el indice dentro de los archivos de propiedades.
     * @param idComponente Indice del Componente.
     * @param x Posición Horizontal en unidades matriciales del inicio de la ubicación del Componente.
     * @param y Posición Vertical en unidades matriciales del inicio de la ubicación del Componente.
     * @param zona Grupo o zona del área donde será ubicado el Componente.
     * @param generador Estado que indica si el componete es Generador.
     */
    public PlantillaComponente(int id, int idComponente, int x, int y, int zona, boolean generador) {        
        this.id = id;
        this.idComponente = idComponente;
        this.x = x;
        this.y = y;
        this.zona = zona;
        this.generador = generador;
        this.idConstructor = -1;           
        this.objGridBagConstraints = new GridBagConstraints();
        this.objInternalProperties = new ArrayList<InternalProperty>();
    }

    /**
     * Constructor que inicializa propiedades y la referencia a la Plantilla Padre.
     * @param id Propiedad utilizada para identificar el indice dentro de los archivos de propiedades.
     * @param idComponente Indice del Componente.
     * @param x Posición Horizontal en unidades matriciales del inicio de la ubicación del Componente.
     * @param y Posición Vertical en unidades matriciales del inicio de la ubicación del Componente.
     * @param zona Grupo o zona del área donde será ubicado el Componente.
     * @param generador Estado que indica si el componete es Generador.
     * @param idConstructor Indice del Constrcutor que inicializará el Componente.
     */
    public PlantillaComponente(int id, int idComponente, int x, int y, int zona, boolean generador, int idConstructor) {        
        this.id = id;
        this.idComponente = idComponente;
        this.x = x;
        this.y = y;
        this.zona = zona;
        this.generador = generador;
        this.idConstructor = idConstructor;        
        this.objInternalProperties = new ArrayList<InternalProperty>();
    }

    /**
     * Devuelve el Estado que indica si el componete es Generador.
     * @return Estado que indica si el componete es Generador.
     */
    public boolean isGenerador() {
        return generador;
    }

    /**
     * Establece el Estado que indica si el componete es Generador.
     * @param generador Estado que indica si el componete es Generador.
     */
    public void setGenerador(boolean generador) {
        this.generador = generador;
    }

    /**
     * Devuelve el Alto en unidades matrciales del Componente.
     * @return Alto en unidades matrciales del Componente.
     */
    public int getHeight() {
        return height;
    }

    /**
     * Establece el Alto en unidades matrciales del Componente.
     * @param height Alto en unidades matrciales del Componente.
     */
    public void setHeight(int height) {
        this.height = height;
        this.objGridBagConstraints.gridheight = this.height;
    }   

    /**
     * Devuelve el Id del Componente.
     * @return Id del Componente.
     */
    public int getIdComponente() {
        return idComponente;
    }

    /**
     * Establece el Id del Componente.
     * @param idComponente Id del Componente.
     */
    public void setIdComponente(int idComponente) {
        this.idComponente = idComponente;
    }

    /**
     * Devuelve el Id del Constructor que inicializará el Componente.
     * @return Id del Constructor que inicializará el Componente.
     */
    public int getIdConstructor() {
        return idConstructor;
    }

    /**
     * Establece el Id del Constructor que inicializará el Componente.
     * @param idConstructor Id del Constructor que inicializará el Componente.
     */
    public void setIdConstructor(int idConstructor) {
        this.idConstructor = idConstructor;
    }

    /**
     * Devuelve el Id de la Plantilla del Componente.
     * @return Id de la Plantilla del Componente.
     */
    public int getIdPlantilla() {
        return idPlantilla;
    }

    /**
     * Establece el Id de la Plantilla del Componente.
     * @param idPlantilla Id de la Plantilla del Componente.
     */
    public void setIdPlantilla(int idPlantilla) {
        this.idPlantilla = idPlantilla;
    }

    /**
     * Devuelve el Estado que indica si la Plantilla del Componente ha sido 
     * incluida en la distribución de la representación actual.
     * @return Estado que indica si la Plantilla del Componente ha sido 
     * incluida en la distribución de la representación actual.
     */
    public boolean isIncluido() {
        return this.incluido;
    }

    /**
     * Establece el Estado que indica si la Plantilla del Componente ha sido 
     * incluida en la distribución de la representación actual.
     * @param incluido Estado que indica si la Plantilla del Componente ha sido 
     * incluida en la distribución de la representación actual.
     */
    public void setIncluido(boolean incluido) {
        this.incluido = incluido;        
    }    

    /**
     * Devuelve el Constructor utilizado por la Plantilla para instanciar el
     * <code>Componente</code>.
     * @return Constructor utilizado por la Plantilla para instanciar el
     * <code>Componente</code>.
     */
    public ObjetoConstructor getObjConstructor() {
        return objConstructor;
    }

    /**
     * Establece el Constructor utilizado por la Plantilla para instanciar el
     * <code>Componente</code>.
     * @param objConstructor Constructor utilizado por la Plantilla para instanciar el
     * <code>Componente</code>.
     */
    public void setObjConstructor(ObjetoConstructor objConstructor) {
        this.objConstructor = objConstructor;
    }

    /**
     * Devuelve los Parametros del Constructor utilizado por la Plantilla para instanciar el
     * <code>Componente</code>.
     * @return Parametros del Constructor utilizado por la Plantilla para instanciar el
     * <code>Componente</code>.
     */
    public ArrayList<String> getObjParametrosConstructor() {
        return objParametrosConstructor;
    }

    /**
     * Establece los Parametros del Constructor utilizado por la Plantilla para instanciar el
     * <code>Componente</code>.
     * @param objParametrosConstructor Parametros del Constructor utilizado por la Plantilla para instanciar el
     * <code>Componente</code>.
     */
    public void setObjParametrosConstructor(ArrayList<String> objParametrosConstructor) {
        this.objParametrosConstructor = objParametrosConstructor;
    }       

    /**
     * Devuelve el Estado que indica si la Plantilla del <code>Componente</code>
     * es requerida en una representación. Este estado es diferente al estado
     * Incluido por lo tanto no debe confundirse su utilización. El estado
     * incluido es prioritario y determina la utilización de la plantilla el
     * estado requerido indica que el componente solo tiene una forma de representación
     * en un plantilla especifica.
     * @return Estado que indica si la Plantilla del <code>Componente</code>
     * es requerida en una representación. Este estado es diferente al estado
     * Incluido por lo tanto no debe confundirse su utilización. El estado
     * incluido es prioritario y determina la utilización de la plantilla el
     * estado requerido indica que el componente solo tiene una forma de representación
     * en un plantilla especifica.
     */
    public boolean isRequerido() {
        return requerido;
    }

    /**
     * Establece el Estado que indica si la Plantilla del <code>Componente</code>
     * es requerida en una representación. Este estado es diferente al estado
     * Incluido por lo tanto no debe confundirse su utilización. El estado
     * incluido es prioritario y determina la utilización de la plantilla el
     * estado requerido indica que el componente solo tiene una forma de representación
     * en un plantilla especifica.
     * @param requerido Estado que indica si la Plantilla del <code>Componente</code>
     * es requerida en una representación. Este estado es diferente al estado
     * Incluido por lo tanto no debe confundirse su utilización. El estado
     * incluido es prioritario y determina la utilización de la plantilla el
     * estado requerido indica que el componente solo tiene una forma de representación
     * en un plantilla especifica.
     */
    public void setRequerido(boolean requerido) {
        this.requerido = requerido;
    }

    /**
     * Devuelve el Ancho que ocupará el <code>Componente</code> en la Representación.
     * @return Ancho que ocupará el <code>Componente</code> en la Representación.
     */
    public int getWidth() {
        return width;
    }

    /**
     * Establece el Ancho que ocupará el <code>Componente</code> en la Representación.
     * @param width Ancho que ocupará el <code>Componente</code> en la Representación.
     */
    public void setWidth(int width) {
        this.width = width;
        this.objGridBagConstraints.gridwidth = this.width;
    }

    /**
     * Devuelve la Coordenada inicial del eje X en la cuál se ubicará el <code>Componente</code>. 
     * @return Coordenada inicial del eje X en la cuál se ubicará el <code>Componente</code>. 
     */
    public int getX() {
        return x;
    }

    /**
     * Establece la Coordenada inicial del eje X en la cuál se ubicará el <code>Componente</code>. 
     * @param x Coordenada inicial del eje X en la cuál se ubicará el <code>Componente</code>. 
     */
    public void setX(int x) {
        this.x = x;
        this.objGridBagConstraints.gridx = this.x;
    }

    /**
     * Devuelve la Coordenada inicial del eje Y en la cuál se ubicará el <code>Componente</code>. 
     * @return Coordenada inicial del eje Y en la cuál se ubicará el <code>Componente</code>. 
     */
    public int getY() {
        return y;
    }

    /**
     * Establece la Coordenada inicial del eje Y en la cuál se ubicará el <code>Componente</code>. 
     * @param y Coordenada inicial del eje Y en la cuál se ubicará el <code>Componente</code>. 
     */
    public void setY(int y) {
        this.y = y;
        this.objGridBagConstraints.gridy = this.y;
    }

    /**
     * Devuelve la Zona de ubicación del <code>Componente</code> en la distribución.
     * La zona se define como un subgrupo dentro de una distribución la cual
     * pueder ser identificada por compartir propiedades similares entre los
     * <code>Componente</code>s que la conforman Ej: zona por ubicación, tamaño etc.
     * @return Zona de ubicación del <code>Componente</code> en la distribución.
     * La zona se define como un subgrupo dentro de una distribución la cual
     * pueder ser identificada por compartir propiedades similares entre los
     * <code>Componente</code>s que la conforman Ej: zona por ubicación, tamaño etc.
     */
    public int getZona() {
        return zona;
    }

    /**
     * Establece la Zona de ubicación del <code>Componente</code> en la distribución.
     * La zona se define como un subgrupo dentro de una distribución la cual
     * pueder ser identificada por compartir propiedades similares entre los
     * <code>Componente</code>s que la conforman Ej: zona por ubicación, tamaño etc.
     * @param zona Zona de ubicación del <code>Componente</code> en la distribución.
     * La zona se define como un subgrupo dentro de una distribución la cual
     * pueder ser identificada por compartir propiedades similares entre los
     * <code>Componente</code>s que la conforman Ej: zona por ubicación, tamaño etc.
     */
    public void setZona(int zona) {
        this.zona = zona;
    }

    /**
     * 
     * @return
     */
    public int getRepresentacion() {
        return representacion;
    }

    /**
     * 
     * @param representacion
     */
    public void setRepresentacion(int representacion) {
        this.representacion = representacion;
    }
    
    

    @Override
    public String toString() {
        return "PlantillaComponente: " + "id = " + id + " Generador = " + generador;
    }

    @Override
    public int compareTo(Object o) {
        try{
            PlantillaComponente obj = (PlantillaComponente) o;
            if(this.id > obj.id)
                return 1;
            else if(this.id < obj.id)
                return -1;
            else
                return 0;
        }catch(java.lang.NullPointerException ex){}
        return 0;
    }

    @Override
    public Object clone() {
        try{
            PlantillaComponente objClon = null;        
            //----------------------------------------------------------
            //Primero se Clona la Referencia Local
            //----------------------------------------------------------
            objClon = (PlantillaComponente) super.clone();        
            //----------------------------------------------------------
            //Ahora se hace la Clonación Profunda
            //----------------------------------------------------------        
            if(this.objConstructor != null){
                ((PlantillaComponente)objClon).objConstructor = objConstructor;
            }
            if(this.objGridBagConstraints != null){
                ((PlantillaComponente)objClon).objGridBagConstraints = (GridBagConstraints) objGridBagConstraints.clone();
            }
            if(objParametrosConstructor != null){
                ((PlantillaComponente)objClon).objParametrosConstructor = objParametrosConstructor;
            }
            if(this.objInternalProperties != null){
                ((PlantillaComponente)objClon).objInternalProperties = (ArrayList<InternalProperty>) objInternalProperties.clone();
            }
            return objClon;
        }catch(java.lang.NullPointerException ex){}
        return null;
    } 
    
    /**
     * Devuelve la Colección de Propiedades Internas de Configuración.
     * @return Colección de Propiedades Internas de Configuración.
     */
    public ArrayList<InternalProperty> getObjInternalProperties() {
        return objInternalProperties;
    }

    /**
     * Establece la Colección de Propiedades Internas de Configuración.
     * @param objInternalProperties Colección de Propiedades Internas de Configuración.
     */
    public void setObjInternalProperties(ArrayList<InternalProperty> objInternalProperties) {
        this.objInternalProperties = objInternalProperties;
    }

    /**
     * Devuelve el GridBagConstraints asigando a la Plantilla del <code>Componente</code>.
     * @return GridBagConstraints asigando a la Plantilla del <code>Componente</code>.
     */
    public GridBagConstraints getObjGridBagConstraints() {
        return objGridBagConstraints;
    }

    /**
     * Establece el GridBagConstraints asigando a la Plantilla del <code>Componente</code>.
     * @param objGridBagConstraints GridBagConstraints asigando a la Plantilla del <code>Componente</code>.
     */
    public void setObjGridBagConstraints(GridBagConstraints objGridBagConstraints) {
        this.objGridBagConstraints = objGridBagConstraints;
    }
    
    /**
     * Actualiza el <code>GridBadConstraints</code> por medio de su <code>Componente</code>
     * incluido teniendo en cuenta la posición inicial.
     * @param objComponenteIncluido Referencia al <code>Componente</code> que representa la <code>PlantillaComponente</code>.
     * @param objPlantillaCombo <code>PlantillaCombo</code> que representa la Distribución Grupal 
     * Actual Seleccionada.
     * @param updatePosicionInicial Determina si se actualiza la posición del
     * <code>GridBadConstraints</code> por medio de la Posición local de la
     * <code>PlantillaComponente</code>.
     */
    public void updateGridBadConstraintsFromComponente(Componente objComponenteIncluido, PlantillaCombo objPlantillaCombo, boolean updatePosicionInicial){
       if(objComponenteIncluido != null && objPlantillaCombo != null){
           
            this.objGridBagConstraints = (GridBagConstraints) objComponenteIncluido.getObjGridBagConstraints().clone();        
            //Se evalua el Tipo de Componente, si es un Componente distribuidor 
            //Se extrae la Colección de Componentes Distribuidos
            if(objComponenteIncluido instanceof ComponenteDistribuidor){
                ComponenteDistribuidor objComponenteDistribuidor = (ComponenteDistribuidor) objComponenteIncluido;
                int gridwidth = 0;
                int gridheight = 0;
                ArrayList<GridBagConstraints> objArrayGridBagConstraints = new ArrayList<GridBagConstraints>();
                for(Componente objComponente : objComponenteDistribuidor.getComponentesDistribuidos()){
                    //Se Obtiene el GridBagConstraints de la Plantilla Activa en el Componente
                    //Ya que es indiferente e independiente a la Plantilla de su Distribuidor Padre
                    //GridBagConstraints objTempGridBagConstraints = objComponente.getGridBagConstraintsPorPlantillaCombo(objPlantillaCombo);
                    GridBagConstraints objTempGridBagConstraints = objComponente.getGridBagConstraintsActiva();
                    //Se Asignan los Valores Iniciales, teniendo en cuenta que son relativos
                    //a la inicializacion, es decir sirven para generar una distribución inicial pero
                    //estos valores podrán cambiar en la Configuración de cada Distribuidor.
                    gridwidth += objTempGridBagConstraints.gridwidth;
                    gridheight += objTempGridBagConstraints.gridheight;
                    objArrayGridBagConstraints.add(objGridBagConstraints);
                }
                GridBagConstraints objGridBagConstraintsNormalizada = getGridBagConstraintsNormalizada(objArrayGridBagConstraints, gridwidth, gridheight);
                if(objGridBagConstraintsNormalizada != null){
                    this.objGridBagConstraints.gridheight = objGridBagConstraintsNormalizada.gridheight;
                    this.objGridBagConstraints.gridwidth = objGridBagConstraintsNormalizada.gridwidth;
                }
                else{
                    this.objGridBagConstraints.gridheight = gridheight;
                    this.objGridBagConstraints.gridwidth = gridwidth;
                }
                objComponenteDistribuidor.getObjGridBagConstraints().gridheight = gridheight;
                objComponenteDistribuidor.getObjGridBagConstraints().gridwidth = gridwidth;
            }
            if(updatePosicionInicial){
                this.objGridBagConstraints.gridx = this.x;
                this.objGridBagConstraints.gridy = this.y;
            }        
        }else{
            UtilRender.printlnConsole("Componente incluido o plantilla  Nulo.", UtilRender.ACTION_NORMAL, false);            
        
        }
    }
    
    public GridBagConstraints getGridBagConstraintsNormalizada(ArrayList<GridBagConstraints> objArrayGridBagConstraints, int filas, int columnas){
        try{
            Object[][] tabla = new Object[filas][columnas];
            ArrayList<Integer> objSizeFilas = new ArrayList<Integer>();
            ArrayList<Integer> objSizeColumnas = new ArrayList<Integer>();
            //Primero se ubican todos los GridBagConstraints en la matriz
            for(GridBagConstraints objTempGridBagConstraints : objArrayGridBagConstraints){
                ubicarIntoTableGridBagConstraints(tabla, objTempGridBagConstraints);
            }
            //Ahora empieza el proceso de normalizacion de la tabla
            //inciando por obtener el tamño ocupado por filas
            for(int i = 0; i < filas; i++){
                int size = 0;
                for(int j = 0; j < columnas; j++){                    
                    if(tabla[i][j] instanceof String){
                       size++; 
                    }                    
                    
                }
                objSizeFilas.add(size);
            }
            //luego se repite el proceso con las columnas
            for(int i = 0; i < columnas; i++){
                int size = 0;
                for(int j = 0; j < filas; j++){
                    if(tabla[j][i] instanceof String){
                        size++; 
                    }                    
                }            
                objSizeColumnas.add(size);
            }
            //Se obtienen los maximos
            int maxWidth = Collections.max(objSizeColumnas);
            int maxHeight = Collections.max(objSizeFilas);
            GridBagConstraints objNew = new GridBagConstraints();
            objNew.gridwidth    = maxWidth;
            objNew.gridheight   = maxHeight;
            return objNew;
        }catch(java.lang.Exception ex){                       
        }         
        return null;
    } 
    
    public boolean ubicarIntoTableGridBagConstraints(Object[][] objModeloTablaDistribucion, GridBagConstraints objTempGridBagConstraints){
        try{            
            int posicionX = objTempGridBagConstraints.gridx;
            int posicionY = objTempGridBagConstraints.gridy; 
            int filas = objTempGridBagConstraints.gridheight;
            int columnas = objTempGridBagConstraints.gridwidth;
            for(int i = 0; i < filas; i++){//coordenadas y
                for(int j = 0; j < columnas; j++){//coordenadas x
                    objModeloTablaDistribucion[posicionY + i][posicionX + j] = "0";
                }
            }
            return true;
        }catch(java.lang.ArrayIndexOutOfBoundsException ex){            
        }catch(java.lang.NullPointerException ex){            
        }
        return false;
    }
}
